package com.leetcode.multithreading.anyan;

import java.util.concurrent.atomic.AtomicInteger;

/**
 * volatile是通过Intel的MESI协议保证了
 * 共享变量缓存一致性的问题
 */
public class Jm_VolatileTest {
    /**
     * MESI协议的核心思想：
     * 当CPU写入数据时，如果发现操作的是共享变量，在其他CPU中也存在该共享变量当副本，
     * 会发出信号通知其他CPU将该变量当缓存行设置为无效状态，
     * 因此其他CPU需要读取这个变量时，发现自己缓存中的该变量的副本是无效的，会重新从内存中读取
     */
    private boolean flag = true;//共享变量
    public boolean getFlag () {
        return flag;
    }
    public void setFlag (boolean flag) {
        this.flag = flag;
    }

    public static class ChangeFlag extends Thread {
        private Jm_VolatileTest jm;

        public ChangeFlag (Jm_VolatileTest jm) {
            this.jm = jm;
        }

        public void changeFlag (boolean flag) {
            jm.flag = flag;
        }

        @Override
        public void run() {
            while (jm.flag) {
                System.out.println(this.getName() + "共享变量flag is：true");
            }
            System.out.println(this.getName() + "共享变量flag is：false");
        }
    }

    public static void main(String[] args) throws Exception {
//        test1();
        test2();
    }

    public static void test1() throws Exception {
        Jm_VolatileTest jm = new Jm_VolatileTest();//此时flag默认是true
        ChangeFlag cf = new ChangeFlag(jm);
        cf.start();
        ChangeFlag cf2 = new ChangeFlag(jm);
        cf2.start();
        Thread.sleep(10);
//        jm.flag = false;
        cf.changeFlag(false);
        ChangeFlag cf3 = new ChangeFlag(jm);
        cf3.start();
    }


    /**
     * 以下是test2例子
     * 使用volatile保证了共享变量inc的可见性，为什么结果不是10000？
     * 因为虽然保证了可见性，但是没能保证原子性
     * （自增操作不具备原子性，它包括读取变量的原始值、进行加1操作、写入工作内存。
     * 那么就是说自增操作的三个子操作可能会分割开执行，就有可能导致这种情况出现）
     *
     * 解决方案在最下方
     */
    public volatile int inc = 0;

    public void increase() {
        inc++;
    }

    public static void test2() throws Exception {
        final Jm_VolatileTest test = new Jm_VolatileTest();
        for(int i=0;i<10;i++){
            new Thread(){
                public void run() {
                    for(int j=0;j<1000;j++)
                        test.increase();
                }
            }.start();
        }

        Thread.sleep(1000);
        System.out.println(test.inc);
    }

    /**
     * 方案一：
     * public synchronized void increase() {
     *         inc++;
     * }
     */

    /**
     * 方案二：
     *     public int inc = 0;
     *     ReentrantLock lock = new ReentrantLock();
     *
     *     public void increase() {
     *         lock.lock();
     *         try {
     *             inc++;
     *         } finally {
     *             lock.unlock();
     *         }
     *     }
     */

    /**
     * 方案三：
     * 在java 1.5的java.util.concurrent.atomic包下提供了一些原子操作类，
     * 即对基本数据类型的 自增（加1操作），自减（减1操作）、
     * 以及加法操作（加一个数），减法操作（减一个数）进行了封装，保证这些操作是原子性操作。
     * atomic是利用CAS来实现原子性操作的（Compare And Swap），
     * CAS实际上是利用处理器提供的CMPXCHG指令实现的，
     * 而处理器执行CMPXCHG指令是一个原子性操作。
     *
     *     public AtomicInteger inc = new AtomicInteger();
     *
     *     public void increase() {
     *         inc.incrementAndGet();
     *     }
     */

}


